package gang.org.springframework.framework.asm;

import gang.org.springframework.framework.core.type.classreading.GangSimpleAnnotationMetadata;
import gang.org.springframework.framework.core.type.classreading.GangSimpleMethodMetadata;
import gang.org.springframework.framework.metadata.GangMergedAnnotation;
import gang.org.springframework.framework.metadata.GangMergedAnnotations;
import gang.org.springframework.framework.type.GangMethodMetadata;
import gang.org.springframework.framework.util.GangClassUtils;
import gang.org.springframework.framework.util.GangStringUtils;
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * visit [ visitSource ] [ visitModule ][ visitNestHost ][ visitPermittedSubclass ][ visitOuterClass ] ( visitAnnotation | visitTypeAnnotation | visitAttribute )* ( visitNestMember | visitInnerClass | visitRecordComponent | visitField | visitMethod )* visitEnd
 * */
public class GangSimpleAnnotationMetadataReadingVisitor extends ClassVisitor {

    /**
     * 业务意义，当前字节码，全路径类名
     * */
    private String className = "";

    private final ClassLoader classLoader;

    private GangSimpleAnnotationMetadata metadata;

    private Source source;

    private List<GangMergedAnnotation<?>> annotations = new ArrayList<>();

    private Set<String> memberClassNames = new LinkedHashSet<>(4);

    private String enclosingClassName;

    private boolean independentInnerClass;

    private String superClassName;

    private List<GangSimpleMethodMetadata> annotatedMethods = new ArrayList<>();

    public GangSimpleAnnotationMetadataReadingVisitor(ClassLoader classLoader) {

        super(Opcodes.ASM5);
        this.classLoader = classLoader;
    }

    /**
     * TODO
     * 执行顺序：1
     * @param version int 52
     * @param access int 33
     * @param name String “gang/org/springframework/boot/autoconfigure/web/servlet/GangServletWebServerFactoryAutoConfiguration”
     * @param signature String null
     * @param superName "java/lang/Object"
     * @param interfaces {}
     * */
    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        this.className = toClassName(name);
        if (superName != null) {
            this.superClassName = toClassName(superName);
        }
    }

    /**
     * TODO
     * 执行顺序：2
     * */
    @Override
    public void visitOuterClass(String owner, String name, String desc) {
        this.enclosingClassName = toClassName(owner);
        System.out.println("step in visitOuterClass");
    }

    /**
     * TODO
     * @param desc 注解描述符 E.g Lgang/org/springframework/framework/annotation/GConfiguration;
     * 执行顺序：3
     * */
    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {

        AnnotationVisitor gangAnnotationVisitor = GangMergedAnnotationReadingVisitor.get(this.classLoader,
                getSource(),
                desc,
                visible,
                this.annotations::add);
        return gangAnnotationVisitor;
    }

    /**
     * TODO
     * 执行顺序：4
     *
     * 业务意义
     * 场景一
     * 1，工厂类中，定义了内部类，对内部类名称收集
     * @param name 内部类全路径名称 E.g gang/org/springframework/framework/web/servlet/GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration
     * @param outerName 主类全路径名称 E.g gang/org/springframework/framework/web/servlet/GangWebMvcAutoConfiguration
     * @param innerName 主类名称 E.g GangEnableWebMvcConfiguration
     *
     * 场景二
     * 获取同样值（内类）
     * */
    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        if (outerName != null) {
            String className = toClassName(name);
            String outerClassName = toClassName(outerName);

            /**
             * 业务意义
             * this.className 理解为工厂主类
             * className 理解为工厂主类内部类
             * */
            if (this.className.equals(className)) {
                this.enclosingClassName = outerClassName;
                this.independentInnerClass = ((access & Opcodes.ACC_STATIC) != 1);
            }else if (this.className.equals(outerClassName)){
                this.memberClassNames.add(className);
            }
        }
    }




    /**
     * TODO
     * 执行顺序：5
     * EnableWebMvcConfiguration -> 获取@Bean method 9
     * EnableWebMvcConfiguration.requestMappingHandlerAdapter()
     * EnableWebMvcConfiguration.requestMappingHandlerMapping()
     * EnableWebMvcConfiguration.welcomePageHandlerMapping()
     * EnableWebMvcConfiguration.localeResolver()
     * EnableWebMvcConfiguration.themeResolver()
     * EnableWebMvcConfiguration.flashMapManager()
     * EnableWebMvcConfiguration.mvcConversionService()
     * EnableWebMvcConfiguration.mvcValidator()
     * EnableWebMvcConfiguration.mvcContentNegotiationManager()
     *
     * */
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

        //TODO 补充业务判断

        return new GangSimpleMethodMetadataReadingVisitor(
                this.classLoader, this.className, access, name, desc, this.annotatedMethods::add);
    }

    /**
     * TODO
     * 执行顺序：6
     * */
    @Override
    public void visitEnd() {

        String[] memberClassName = GangStringUtils.toStringArray(this.memberClassNames);

        /**
         * 业务意义
         * GangMergedAnnotations 当前字节码class中，所包含的注解集合
         * 目的在于创建类中，所有包含的注解对象
         * E.g 类-> gang.org.springframework.framework.web.servlet.GangWebMvcAutoConfiguration
         * E.g 该类中的注解-> gang.org.springframework.framework.annotation.GConfiguration
         * */
        GangMergedAnnotations annotations = GangMergedAnnotations.of(this.annotations);

        GangMethodMetadata[] methodMetadata = this.annotatedMethods.toArray(new GangMethodMetadata[0]);

        this.metadata = new GangSimpleAnnotationMetadata(
                this.className,
                0,
                this.enclosingClassName,
                superClassName,
                this.independentInnerClass,
                null,
                memberClassName,
                methodMetadata,
                annotations);

    }

    @Override
    public String toString() {
        return super.toString();
    }

    public GangSimpleAnnotationMetadata getMetadata() {
        return this.metadata;
    }

    private String toClassName(String name){
        return GangClassUtils.convertResourcePathToClassName(name);
    }

    private static final class Source{
        private final String className;

        public Source(String className) {
            this.className = className;
        }
    }

    private Source getSource(){
        Source source = this.source;

        if (source == null) {
            source = new Source(this.className);
            this.source = source;
        }
        return source;
    }


}
